<!doctype html>



  


<html class="theme-next mist use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="java,Concurrent,Volatile," />








  <link rel="shortcut icon" type="image/x-icon" href="/blog/favicon.ico?v=5.1.0" />






<meta name="description" content="volatile相比synchronize的优势不足volatile保证了多线程情况下的可见性，并且在一定程度上保证有序性。但是并不能保证原子性。volatile在实现部分线程安全的功能中，避免了线程之间的切换和调度，因此较之synchronize性能有很大的提升。 系统的内存模型系统处理指令的过程大致为：CPU从主内存中加载数据，然后进行处理，再写入到内存中。我们知道CPU处理数据速度比较快，而">
<meta name="keywords" content="java,Concurrent,Volatile">
<meta property="og:type" content="article">
<meta property="og:title" content="Volatile实现原理及使用模式">
<meta property="og:url" content="https://gitee.com/shuhao/2018/02/09/Volatile实现原理及使用模式/index.html">
<meta property="og:site_name" content="Niki">
<meta property="og:description" content="volatile相比synchronize的优势不足volatile保证了多线程情况下的可见性，并且在一定程度上保证有序性。但是并不能保证原子性。volatile在实现部分线程安全的功能中，避免了线程之间的切换和调度，因此较之synchronize性能有很大的提升。 系统的内存模型系统处理指令的过程大致为：CPU从主内存中加载数据，然后进行处理，再写入到内存中。我们知道CPU处理数据速度比较快，而">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://gitee.com/blog/images/java/concurrenty/memory_cache_model.jpg">
<meta property="og:updated_time" content="2018-08-09T08:15:58.980Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Volatile实现原理及使用模式">
<meta name="twitter:description" content="volatile相比synchronize的优势不足volatile保证了多线程情况下的可见性，并且在一定程度上保证有序性。但是并不能保证原子性。volatile在实现部分线程安全的功能中，避免了线程之间的切换和调度，因此较之synchronize性能有很大的提升。 系统的内存模型系统处理指令的过程大致为：CPU从主内存中加载数据，然后进行处理，再写入到内存中。我们知道CPU处理数据速度比较快，而">
<meta name="twitter:image" content="https://gitee.com/blog/images/java/concurrenty/memory_cache_model.jpg">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Mist',
    sidebar: {"position":"right","display":"post","offset":12,"offset_float":0,"b2t":false,"scrollpercent":false},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://gitee.com/shuhao/2018/02/09/Volatile实现原理及使用模式/"/>





  <title> Volatile实现原理及使用模式 | Niki </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?cb9f0585e406f878cb2f29c89ad3cdb7";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>










  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Niki</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">天道酬勤</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-categories"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/shuhao/blog/2018/02/09/Volatile实现原理及使用模式/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Fang ShuHao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/head.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Niki">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                Volatile实现原理及使用模式
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-02-09T10:14:08+08:00">
                2018-02-09
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/java/" itemprop="url" rel="index">
                    <span itemprop="name">java</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <h2 id="volatile相比synchronize的优势不足"><a href="#volatile相比synchronize的优势不足" class="headerlink" title="volatile相比synchronize的优势不足"></a>volatile相比synchronize的优势不足</h2><p>volatile保证了多线程情况下的可见性，并且在一定程度上保证有序性。但是并不能保证原子性。volatile在实现部分线程安全的功能中，避免了线程之间的切换和调度，因此较之synchronize性能有很大的提升。</p>
<h2 id="系统的内存模型"><a href="#系统的内存模型" class="headerlink" title="系统的内存模型"></a>系统的内存模型</h2><p>系统处理指令的过程大致为：CPU从主内存中加载数据，然后进行处理，再写入到内存中。我们知道CPU处理数据速度比较快，而从内存中读/写数据的速度很慢；从而限制了CPU执行指令的效率。所以，CPU中就有<strong><em>高速缓存</em></strong>用来提高指令执行的效率。<br>引入CPU高速缓存虽然解决了指令执行效率的问题，但是也引入了<strong><em>缓存一致性</em></strong>的问题：在多核处理器中，某个CPU执行指令后，数据写到其高速缓存中，来不及刷到主内存中，则导致其他CPU高速缓存中的数据与最终数据不一致。<br>为了解决缓存不一致性有两种方式：</p>
<ol>
<li>总线锁机制</li>
<li>缓存一致协议<br>总线锁机制，通过在总线上加LOCK锁的形式来解决缓存不一致的问题。因为CPU和其他部件进行通信都是通过总线来进行的，对总线加锁，则阻塞了其他CPU对其他部件的访问（内存），并且当获得锁的CPU执行完指令后，释放锁之前会把内存修改值刷新到内存。这种机制虽然解决了缓存一致性，但是会导致效率低下。类似于synchronize加锁的机制。<br>缓存一致协议中最出名的是Intel的<a href="http://shuhao.gitee.io/blog/2018/08/01/CPU%E7%BC%93%E5%AD%98%E4%B8%80%E8%87%B4%E6%80%A7%E5%8D%8F%E8%AE%AE/" target="_blank" rel="external">MESI协议</a>。协议保证每个CPU都保存共享变量的一个副本。当CPU写数据时，发现该变量是共享变量（其他CPU中也保存该变量的副本），会向其他的CPU发出信号，通知其他CPU将该变量的缓存行置为无效状态。当CPU从高速缓存中读取变量时会发现该变量缓存行已经无效，从而重新去内存中获取变量的值。<img src="/blog/images/java/concurrenty/memory_cache_model.jpg">
</li>
</ol>
<h2 id="java并发编程的原子性，可见性，有序性"><a href="#java并发编程的原子性，可见性，有序性" class="headerlink" title="java并发编程的原子性，可见性，有序性"></a>java并发编程的原子性，可见性，有序性</h2><ul>
<li>原子性<br>  多个操作时，不会被中途中断，要么都成功，要么都失败。在JAVA中只有单一的读/写操作具有原子性。（典型场景为：银行账户之间转账）</li>
<li>可见性<br>  当多个线程访问一个共享变量当一个线程修改内存中的值后，其他线程能够立即看见修改后的值。（一个线程修改了内存中的值后立即刷新，并通知其他线程更新工作内存中的值）</li>
<li>有序性<br>  保证语句的执行顺序，不被JVM重排。JVM会对语句进行指令重排，如果两条执行语句间有<strong><em>数据依赖</em></strong>则不会进行指令重排。指令重排不会影响单线程程序的执行，却会影响多线程并发执行的正确性。<br>语句重排遵循<strong>happens-before原则</strong></li>
<li>程序次序规则：一个线程内，按照代码顺序，书写在前面的操作先行发生于书写在后面的操作</li>
<li>锁定规则：一个unLock操作先行发生于后面对同一个锁额lock操作</li>
<li>volatile变量规则：对一个变量的写操作先行发生于后面对这个变量的读操作</li>
<li>传递规则：如果操作A先行发生于操作B，而操作B又先行发生于操作C，则可以得出操作A先行发生于操作C</li>
<li>线程启动规则：Thread对象的start()方法先行发生于此线程的每个一个动作</li>
<li>线程中断规则：对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生</li>
<li>线程终结规则：线程中所有的操作都先行发生于线程的终止检测，我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行</li>
<li>对象终结规则：一个对象的初始化完成先行发生于他的finalize()方法的开始</li>
</ul>
<h2 id="java内存模型"><a href="#java内存模型" class="headerlink" title="java内存模型"></a>java内存模型</h2><p>Java内存模型规定所有的变量都是存在于主内存中，每个线程有自己的工作内存（类似于高速缓存）。线程对变量的所有操作都必须在工作内存中进行，而不能直接对主内存进行操作。并且每个线程不能访问其他线程的工作内存。</p>
<h2 id="volatile实现原理以及-如何确可见性以及有序性"><a href="#volatile实现原理以及-如何确可见性以及有序性" class="headerlink" title="volatile实现原理以及 如何确可见性以及有序性"></a>volatile实现原理以及 如何确可见性以及有序性</h2><p>java代码：<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">instance = <span class="keyword">new</span> Singleton();<span class="comment">//instance是volatile变量</span></span><br></pre></td></tr></table></figure></p>
<p>汇编代码：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">0x01a3de1d: movb $0x0,0x1104800(%esi);</span><br><span class="line">0x01a3de24: lock addl $0x0,(%esp);</span><br></pre></td></tr></table></figure></p>
<p>可以看到汇编代码多了第二行，带了lock前缀的指令。在多核处理器下会引发两件事情</p>
<ol>
<li>将当前处理器缓存行的数据回写到系统内存。</li>
<li>回写操作会使其他CPU里缓存了该内存地址的数据无效。<br>并且lock指令前缀实际相当于一个内存屏障；内存屏障的三个功能</li>
<li>它确保指令重排序时不会亮后面的指令排到内存屏障之前的位置，也不会把内存屏障之后的指令排到内存屏障之前；即在执行内存屏障这句指令时，在它前面的操作已经全部完成。</li>
<li>它会强制将对缓存的修改操作立即写入主内存</li>
<li>如果是写操作，它会导致其他CPU中对应的缓存行无效<br>所以volatile声明的变量，如果有个线程修改了共享变量的值，会是的其他线程缓存该共享变量的值变得无效，并且重新到内存中加载。从而保证了<strong>可见性</strong>。并且由于<strong><em>内存屏障</em></strong>使得volatile声明的变脸语句不被指令重排，保证了其<strong>有序性</strong>。</li>
</ol>
<h2 id="volatile使用实例"><a href="#volatile使用实例" class="headerlink" title="volatile使用实例"></a>volatile使用实例</h2><p>我们看两个实例，理解volatile保证可见性以及有序性的场景</p>
<h3 id="可见性"><a href="#可见性" class="headerlink" title="可见性"></a>可见性</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">##线程1</span><br><span class="line"><span class="keyword">boolean</span> stop = <span class="keyword">false</span> ;</span><br><span class="line"><span class="keyword">while</span>(!stop)&#123;</span><br><span class="line">	doSomething();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">##线程2</span><br><span class="line">stop = <span class="keyword">true</span>;</span><br></pre></td></tr></table></figure>
<p>没用volatile：<br>    上面的实例其实是一种很常见的停止线程的方式。通过一个标志变量，来决定线程的运行。在多线程并发的环境下。线程1可能在运行时将stop=false这个值拷贝到其工作内存中，当线程2再线程2的工作内存中更改stop的值后，还没来得起将值写到系统内存中，线程被挂起<br>则导致线程1不知道stop的值被修改，从而一直循环下去。</p>
<p>使用volatile：<br>当stop使用volatile声明后，线程2修改stop的值会被<strong>立即写入到主内存中</strong>，当线程1<strong>再次读取</strong>stop的值时发现工作内存中缓存的stop的值无效，会再次从注内存中读取stop的值。从而实现了线程2修改后的值对stop立即可见。</p>
<h3 id="有序性"><a href="#有序性" class="headerlink" title="有序性"></a>有序性</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">##线程1</span><br><span class="line">context = loadContext();</span><br><span class="line">init = <span class="keyword">true</span> ;</span><br><span class="line"></span><br><span class="line">##线程2</span><br><span class="line"><span class="keyword">while</span>(!init)&#123;</span><br><span class="line">	sleep();</span><br><span class="line">&#125;</span><br><span class="line">doSomething(context);</span><br></pre></td></tr></table></figure>
<p>没用volatile：<br>因为JVM可能会对指令重排，所以init=true的赋值操作可能会先于loadContext()执行，如果init=true执行后，线程1没有loadContext就被挂起，且线程2得到时间片开始执行，则会导致doSometh没有初始化就开始运行。</p>
<p>使用volatile：<br>当init使用volatile声明后，由于内存屏障保障，init=true之前的指令不会在其之后执行，所以init=true时context一定被初始化了。从而保证了doSomething方法使用的参数一定初始化。</p>
<h2 id="volatile为什么不具备原子特性"><a href="#volatile为什么不具备原子特性" class="headerlink" title="volatile为什么不具备原子特性"></a>volatile为什么不具备原子特性</h2><p>因为volatile每次修改之后都会通知其他线程重新刷新缓存，为什么还是无法保障原子性.<br>下面是一个典型的并发场景下的自加<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">volatile</span> <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                   i+=<span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start() ;</span><br><span class="line">        &#125;</span><br><span class="line">       System.out.println(i);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure></p>
<p>按照我们之前分析的volatile，每个线程修改了共享变量的值都会通知其他线程这样每个线程自加之前都是前一个线程修改后的值那么最后的值应该是1000，实际运行的值都比1000小，每次都不一样，那么到底是为什么volatile没有保证并发情况下的原子性。<br>因为i+=1 其实包含了三个操作</p>
<ol>
<li>线程从系统内存读取i的值到工作内存</li>
<li>线程将i的值加1</li>
<li>将修改后i的值回写到系统内存。这一步不确定什么时候完成<br>假设，当线程1从主内存中读取了i=1写入到工作内存中后被挂起，<br>另外一个线程2从也从主内存中读取i的值i=1，同样写入到工作内存；并且完成加一操作。此时并没有将操作后的值写到系统内存。此时主内存中i=2，<br>当线程1重新运行，线程1工作内存中缓存的i=1，<strong>不会再去主内存读取</strong> ，仍然读取工作内存中缓存i=1做加一操作后，将i=2刷新到主内存。<br>所以，volatile并没有保障操作的原子性。仍然是线程不安全的。</li>
</ol>
<p><strong>（因为没有再次读取主内存，所以并不会发现工作内存中的缓存已经失效。volatile的可见性，只保证一个线程修改了共享变量的值后，会立即将值回写到主内存。缺少类似MESI协议中通知其他CPU的操作，去通知其他的线程刷工作内存的值）</strong></p>
<h2 id="volatile使用模式"><a href="#volatile使用模式" class="headerlink" title="volatile使用模式"></a>volatile使用模式</h2><p>正确使用volatile提供的线程安全，必须满足下面两个条件</p>
<ol>
<li>对变量的写操作不依赖于当前值</li>
<li>该变量没有包含在具有其他变量的不变式中</li>
</ol>
<p>我们知道volatile无法对增量操作提供线程安全，因为x++这个操作不满足第一个条件。虽然x++看上去是一个单独操作，但是其有读取-修改-写入三个步骤组合，而且<strong><em>每次修改都必须要依赖当前的值。</em></strong>所以volatile无法为该操作提供线程安全的保障。</p>
<p>我们来看下另外一个情形<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@NotThreadSafe</span> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NumberRange</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> lower, upper;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getLower</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> lower; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getUpper</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> upper; &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setLower</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">if</span> (value &gt; upper) </span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(...);</span><br><span class="line">        lower = value;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUpper</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">if</span> (value &lt; lower) </span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(...);</span><br><span class="line">        upper = value;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这个一个数值范围类。显然这是一个线程不安全的类。如果使用volatile声明lower 和 upper变量，能使类变成线程安全的么？这也是无法做到的。因为就lower和upper这两个变量违反了第二个条件：<strong>变量没有包含在具有其他变量的不变式中*</strong><br>我们来分析下在并发情况下使用volatile声明两个变量后，这个类为什么还是线程不安全的。<br>假设初始范围为（0,5），有线程1和线程2同时修改这个范围类的一个实例。<br>线程1将lower=0，upper=5初始值读入到其工作内存中缓存，且此时线程2也将lower=0，upper=5读入到其工作内存中缓存。<br>线程1调用setLower将lower的值设置为4，因为upper的值在工作线程中为5所以这个操作是成功的。<br>线程2调用setUpper的值将upper的值设为2，因为lower在线程2中的工作内存中缓存的值为0，（虽然此时主内存中的值可能已经被修改为4，但是线程2已经缓存了lower的值，并不会再去主内存读取新的值）并且也设置成功。<br>最后范围类的值为（4,2）这是不合法的情况。所以setLower和setUpper这两个操作必须是原子性的。volatile并不能保证原子性。</p>
<p>那么我们来看下，正确使用volatile的模式</p>
<h3 id="状态标志（可见性）"><a href="#状态标志（可见性）" class="headerlink" title="状态标志（可见性）"></a>状态标志（可见性）</h3><p>由于volatile的可见性，保障了任意线程修改了共享变量的值后，会立即刷新到主内存，其他线程能立即看到新的值。下面看下这个模式的示例<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">volatile boolean flag = true ;#代码1</span><br><span class="line"></span><br><span class="line">while(!flag)&#123; #代码2</span><br><span class="line">	doSomethint();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>即使多个线程共享flag，无论哪个线程将flag的值修改为false，其他线程在执行到代码2后都会进入到条件中。</p>
<h3 id="一次性安全发布（双重检查锁定）"><a href="#一次性安全发布（双重检查锁定）" class="headerlink" title="一次性安全发布（双重检查锁定）"></a>一次性安全发布（双重检查锁定）</h3><p>被发布的对象必须是线程安全的，或者是有效的不可变对象（有效的不可变意味着对象的状态在发布之后永远不会被修改）。volatile类型的引用可以确保对象的发布形式的可见性，但是如果对象的状态在发布之后将会发生更改，那么就需要额外的同步。<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BackgroundFloobleLoader</span></span>&#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">volatile</span> Flooble thFlooble;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initInBackground</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="comment">//do lots of stuff</span></span><br><span class="line">		theFlooble = <span class="keyword">new</span> Flooble(); <span class="comment">// this is the only write to theFlooble</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SomeOtherClass</span></span>&#123;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doWork</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="keyword">while</span>(<span class="keyword">true</span>)&#123;</span><br><span class="line">			<span class="comment">//do some stuff....</span></span><br><span class="line">			<span class="comment">//use the Flooble, but only if it is ready</span></span><br><span class="line">			<span class="keyword">if</span>(floobleLoader.theFlooble != <span class="keyword">null</span>)&#123;</span><br><span class="line">				doSomethind(floobleLoader.theFlooble);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>确保了在initInBackground()方法执行时，thFlooble这个对象安全发布。</p>
<h3 id="双重锁定"><a href="#双重锁定" class="headerlink" title="双重锁定"></a>双重锁定</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span></span>&#123;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> Singleton instance = <span class="keyword">null</span> ;</span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getInstance</span><span class="params">()</span></span>&#123;</span><br><span class="line">		<span class="keyword">if</span>(instance == <span class="keyword">null</span>)&#123;</span><br><span class="line">			synchronize(Singleton.class)&#123; <span class="comment">//1</span></span><br><span class="line">				<span class="keyword">if</span>(instance == <span class="keyword">null</span>)&#123; <span class="comment">//2</span></span><br><span class="line">					instance = <span class="keyword">new</span> Singleton(); <span class="comment">//3</span></span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">return</span> instance ;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="安全发布问题根源"><a href="#安全发布问题根源" class="headerlink" title="安全发布问题根源"></a>安全发布问题根源</h4><p><strong>无序写入：</strong><br>Java平台的内存模型允许无序写入。例如一行初始化类的代码 A a = new A(); 这行代码做了两件事情：1. 创建了一个A；2. 初始化a对象。也就可能造成 A构造函数体执行之前，实例a成为<strong><em>非null</em></strong>的。即<strong>赋值语句在对象实例化之前调用</strong>。<br>所以代码行 instance = new Singleton(); 中instance可能拿到的是一个未完全初始化的对象（非安全发布的对象）。下面我们来假设如何获取到非安全发布的对象：</p>
<ol>
<li>线程1进入到getInstance方法。</li>
<li>由于instance为null， 线程1在代码行1处获取同步锁。</li>
<li>线程1执行到代码行3处，执行构造函数初始化之前instance已经为非null，但是只是部分初始化并未安全发布。</li>
<li>线程1挂起，线程2执行getInstance 发现instance已经为非null， 并且获得部分初始化了的Singleton实例。使用该实例对象，可能导致系统崩溃。。。。。。</li>
<li>线程1重新获得执行的机会，完成instance对象的初始化。</li>
</ol>
<p><strong>instance = newSingleton()</strong>伪代码<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mem = allocate(); //为单例对象分配内存空间</span><br><span class="line">instance = mem ; //为instance引用赋值，此时instance为非null，但是未初始化</span><br><span class="line">ctorSingleton(instance); //为单例对象通过instance调用构造函数</span><br></pre></td></tr></table></figure></p>
<p>在JAVA2（jdk2）以前对于实例字段是直接在主存储区读写的。所以当一个线程对resource进行分配空间，初始化和调用构造方法时，可能在其他线程中分配空间的动作可见了，而初始化和调用构造函数方法还没有完成。<br>但是在JAVA2以后JMM发生了根本的改变，分配空间，初始化，调用改造方法只会在线程的工作存储区域完成，在没有想主内存复制赋值时，其他线程绝对不可能见到这个过程。而这个字段复制到主内存的过程，更不会有分配空间后，没有初始化或没有调用构造方法的可能。在JAVA中，一切都是按引用的值复制的。想主内存区同步其实就是把线程工作内存中这个已经构造好的对象有压缩堆地址值copy给主内存中那个变量。这个过程对于其他线程，要么resource为null，要么resource为完整的对象。绝对不会把一个已经分配空间却没有构造好的对象让其他线程可见。<a href="http://www.iteye.com/topic/260515" target="_blank" rel="external">详细可参见</a></p>
<h3 id="独立观察"><a href="#独立观察" class="headerlink" title="独立观察"></a>独立观察</h3><p>由于volatile保障了共享变量的可见性，所以当一个变量需要被多个线程反复观察读取时，可以通过volatile来保障其他线程在再次读取该值时永远是一个新值。<br>一下代码展示了身份验证机制如何记忆最近一次登录的用户的名字。将反复使用 lastUser 引用来发布值，以供程序的其他部分使用。<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserManager</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">volatile</span> String lastUser;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">authenticate</span><span class="params">(String user, String password)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> valid = passwordIsValid(user, password);</span><br><span class="line">        <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">            User u = <span class="keyword">new</span> User();</span><br><span class="line">            activeUsers.add(u);</span><br><span class="line">            lastUser = user;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> valid;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="volatile-bean-模式"><a href="#volatile-bean-模式" class="headerlink" title="volatile bean 模式"></a>volatile bean 模式</h3><p>volatile bean 模式适用于将 JavaBeans 作为“荣誉结构”使用的框架。在 volatile bean 模式中，JavaBean 被用作一组具有 getter 和/或 setter 方法 的独立属性的容器。volatile bean 模式的基本原理是：很多框架为易变数据的持有者（例如 HttpSession）提供了容器，但是放入这些容器中的对象必须是线程安全的。<br>在 volatile bean 模式中，JavaBean 的所有数据成员都是 volatile 类型的，并且 getter 和 setter 方法必须非常普通 —— 除了获取或设置相应的属性外，不能包含任何逻辑。此外，对于对象引用的数据成员，引用的对象必须是有效不可变的。（这将禁止具有数组值的属性，因为当数组引用被声明为 volatile 时，只有引用而不是数组本身具有 volatile 语义）。对于任何 volatile 变量，不变式或约束都不能包含 JavaBean 属性。一下代码中的示例展示了遵守 volatile bean 模式的 JavaBean：<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ThreadSafe</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> String firstName;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> String lastName;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">int</span> age;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getFirstName</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> firstName; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getLastName</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> lastName; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getAge</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> age; &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setFirstName</span><span class="params">(String firstName)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">this</span>.firstName = firstName;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setLastName</span><span class="params">(String lastName)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">this</span>.lastName = lastName;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAge</span><span class="params">(<span class="keyword">int</span> age)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="开销较低的读-写锁策略"><a href="#开销较低的读-写锁策略" class="headerlink" title="开销较低的读-写锁策略"></a>开销较低的读-写锁策略</h3><p>如果读操作远远超过写操作，您可以结合使用内部锁和 volatile 变量来减少公共代码路径的开销。以下代码显示线程安全的计数器使用synchronize确保增量操作是原子的，并使用volatile保证当前结果的可见性。<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ThreadSafe</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CheesyCounter</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Employs the cheap read-write lock trick</span></span><br><span class="line">    <span class="comment">// All mutative operations MUST be done with the 'this' lock held</span></span><br><span class="line">    <span class="meta">@GuardedBy</span>(<span class="string">"this"</span>) <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">int</span> value;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getValue</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> value; &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">int</span> <span class="title">increment</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> value++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这种策略将读/写两种操作分开处理。因为写操作违反了volatile安全使用的第一个条件，所以采用synchronize来保障线程安全。在读操作中volatile便可确保当前值的可见性。（读值的时候会去主内存读取）这样避免了读操作也去竞争写操作的锁，从而减少了锁的开销，提升在高并发情况下的效率。</p>
<blockquote>
<blockquote>
<p>参考<br><a href="http://www.cnblogs.com/dolphin0520/p/3920373.html" target="_blank" rel="external">Java并发编程：volatile关键字解析</a><br><a href="https://www.ibm.com/developerworks/cn/java/j-jtp06197.html" target="_blank" rel="external">正确使用 Volatile 变量</a><br><a href="http://www.iteye.com/topic/652440" target="_blank" rel="external">关于延迟加载的双重锁</a><br><a href="http://www.iteye.com/topic/260515" target="_blank" rel="external">用happen-before规则重新审视DCL</a></p>
</blockquote>
</blockquote>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>


    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/blog/tags/java/" rel="tag"># java</a>
          
            <a href="/blog/tags/Concurrent/" rel="tag"># Concurrent</a>
          
            <a href="/blog/tags/Volatile/" rel="tag"># Volatile</a>
          
        </div>
      

      
        
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2018/02/07/Maven可选依赖/" rel="next" title="Maven可选依赖">
                <i class="fa fa-chevron-left"></i> Maven可选依赖
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2018/02/11/红黑树/" rel="prev" title="红黑树（R-B Tree）">
                红黑树（R-B Tree） <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>

          
          </div>
          


          
  <div class="comments" id="comments">
    
      <div id="lv-container" data-id="city" data-uid="MTAyMC8zNjY5NS8xMzIzMA=="></div>
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/blog/images/head.jpg"
               alt="Fang ShuHao" />
          <p class="site-author-name" itemprop="name">Fang ShuHao</p>
           
              <p class="site-description motion-element" itemprop="description"></p>
          
        </div>
        <nav class="site-state motion-element">
        
          
            <div class="site-state-item site-state-posts">
              <a href="/blog/archives">
                <span class="site-state-item-count">135</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-categories">
              <a href="/blog/categories">
                <span class="site-state-item-count">35</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-tags">
              <a href="/blog/tags">
                <span class="site-state-item-count">93</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile相比synchronize的优势不足"><span class="nav-number">1.</span> <span class="nav-text">volatile相比synchronize的优势不足</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#系统的内存模型"><span class="nav-number">2.</span> <span class="nav-text">系统的内存模型</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java并发编程的原子性，可见性，有序性"><span class="nav-number">3.</span> <span class="nav-text">java并发编程的原子性，可见性，有序性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java内存模型"><span class="nav-number">4.</span> <span class="nav-text">java内存模型</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile实现原理以及-如何确可见性以及有序性"><span class="nav-number">5.</span> <span class="nav-text">volatile实现原理以及 如何确可见性以及有序性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile使用实例"><span class="nav-number">6.</span> <span class="nav-text">volatile使用实例</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#可见性"><span class="nav-number">6.1.</span> <span class="nav-text">可见性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#有序性"><span class="nav-number">6.2.</span> <span class="nav-text">有序性</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile为什么不具备原子特性"><span class="nav-number">7.</span> <span class="nav-text">volatile为什么不具备原子特性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#volatile使用模式"><span class="nav-number">8.</span> <span class="nav-text">volatile使用模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#状态标志（可见性）"><span class="nav-number">8.1.</span> <span class="nav-text">状态标志（可见性）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#一次性安全发布（双重检查锁定）"><span class="nav-number">8.2.</span> <span class="nav-text">一次性安全发布（双重检查锁定）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#双重锁定"><span class="nav-number">8.3.</span> <span class="nav-text">双重锁定</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#安全发布问题根源"><span class="nav-number">8.3.1.</span> <span class="nav-text">安全发布问题根源</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#独立观察"><span class="nav-number">8.4.</span> <span class="nav-text">独立观察</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#volatile-bean-模式"><span class="nav-number">8.5.</span> <span class="nav-text">volatile bean 模式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#开销较低的读-写锁策略"><span class="nav-number">8.6.</span> <span class="nav-text">开销较低的读-写锁策略</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Fang ShuHao</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Mist
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    
    
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  




  
  <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.0"></script>



  
  

  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.0"></script>



  



  




	





  





  





  
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  



  
  

  

  

  

  


  

</body>
</html>
